<!DOCTYPE html>












  


<html class="theme-next pisces use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2"/>
<meta name="theme-color" content="#222">












<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />






















<link href="/my_blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/my_blog/css/main.css?v=6.3.0" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/my_blog/images/apple-touch-icon-next.png?v=6.3.0">


  <link rel="icon" type="image/png" sizes="32x32" href="/my_blog/images/favicon-32x32-next.png?v=6.3.0">


  <link rel="icon" type="image/png" sizes="16x16" href="/my_blog/images/favicon-16x16-next.png?v=6.3.0">


  <link rel="mask-icon" href="/my_blog/images/logo.svg?v=6.3.0" color="#222">









<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/my_blog/',
    scheme: 'Pisces',
    version: '6.3.0',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: false,
    fastclick: false,
    lazyload: false,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>


  




  <meta name="description" content="#.NET 中的内存管理和垃圾回收机制 (1) - 垃圾回收 本文参考来源：Memory Management and Garbage Collection in .NET  每次创建新对象时，CLR 都会从托管堆中为该对象分配内存。  只要托管堆中的地址空间可用，运行时就会继续为新对象分配空间。  但是，内存不是无限的。最终垃圾收集器必须执行一个回收才能释放一些内存。  垃圾收集器的优化引擎根据">
<meta name="keywords" content="CSharp">
<meta property="og:type" content="article">
<meta property="og:title" content="Memory Management and Garbage Collection in .NET - 2">
<meta property="og:url" content="http://sdfsd23few23.gitee.io/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/index.html">
<meta property="og:site_name" content="Raymond&#39;s Blog">
<meta property="og:description" content="#.NET 中的内存管理和垃圾回收机制 (1) - 垃圾回收 本文参考来源：Memory Management and Garbage Collection in .NET  每次创建新对象时，CLR 都会从托管堆中为该对象分配内存。  只要托管堆中的地址空间可用，运行时就会继续为新对象分配空间。  但是，内存不是无限的。最终垃圾收集器必须执行一个回收才能释放一些内存。  垃圾收集器的优化引擎根据">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="http://sdfsd23few23.gitee.io/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/gc-triggered.png">
<meta property="og:image" content="http://sdfsd23few23.gitee.io/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/loh-figure-1.jpg">
<meta property="og:image" content="http://sdfsd23few23.gitee.io/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/loh-figure-2.jpg">
<meta property="og:updated_time" content="2018-08-11T06:24:29.772Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Memory Management and Garbage Collection in .NET - 2">
<meta name="twitter:description" content="#.NET 中的内存管理和垃圾回收机制 (1) - 垃圾回收 本文参考来源：Memory Management and Garbage Collection in .NET  每次创建新对象时，CLR 都会从托管堆中为该对象分配内存。  只要托管堆中的地址空间可用，运行时就会继续为新对象分配空间。  但是，内存不是无限的。最终垃圾收集器必须执行一个回收才能释放一些内存。  垃圾收集器的优化引擎根据">
<meta name="twitter:image" content="http://sdfsd23few23.gitee.io/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/gc-triggered.png">






  <link rel="canonical" href="http://sdfsd23few23.gitee.io/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/"/>



<script type="text/javascript" id="page.configurations">
  CONFIG.page = {
    sidebar: "",
  };
</script>

  <title>Memory Management and Garbage Collection in .NET - 2 | Raymond's Blog</title>
  









  <noscript>
  <style type="text/css">
    .use-motion .motion-element,
    .use-motion .brand,
    .use-motion .menu-item,
    .sidebar-inner,
    .use-motion .post-block,
    .use-motion .pagination,
    .use-motion .comments,
    .use-motion .post-header,
    .use-motion .post-body,
    .use-motion .collection-title { opacity: initial; }

    .use-motion .logo,
    .use-motion .site-title,
    .use-motion .site-subtitle {
      opacity: initial;
      top: initial;
    }

    .use-motion {
      .logo-line-before i { left: initial; }
      .logo-line-after i { right: initial; }
    }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/my_blog/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Raymond's Blog</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
    
      
        <p class="site-subtitle">CSharp & Java</p>
      
    
  </div>

  <div class="site-nav-toggle">
    <button aria-label="Toggle navigation bar">
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>



<nav class="site-nav">
  
    <ul id="menu" class="menu">
      
        
        
        
          
          <li class="menu-item menu-item-home">
    <a href="/my_blog/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-home"></i> <br />Startseite</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-tags">
    <a href="/my_blog/tags/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />Tags</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-categories">
    <a href="/my_blog/categories/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-th"></i> <br />Kategorien</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-archives">
    <a href="/my_blog/archives/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />Archiv</a>
  </li>

      
      
    </ul>
  

  

  
</nav>



  



</div>
    </header>

    


    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          
            

          
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://sdfsd23few23.gitee.io/my_blog/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Raymond">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/my_blog/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Raymond's Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">Memory Management and Garbage Collection in .NET - 2
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Veröffentlicht am</span>
              

              
                
              

              <time title="Post created: 2018-08-10 20:45:56" itemprop="dateCreated datePublished" datetime="2018-08-10T20:45:56+08:00">2018-08-10</time>
            

            
              

              
                
                <span class="post-meta-divider">|</span>
                

                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                
                  <span class="post-meta-item-text">Edited on</span>
                
                <time title="Updated at: 2018-08-11 14:24:29" itemprop="dateModified" datetime="2018-08-11T14:24:29+08:00">2018-08-11</time>
              
            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">in</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/my_blog/categories/Coding/" itemprop="url" rel="index"><span itemprop="name">Coding</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <p>#.NET 中的内存管理和垃圾回收机制 (1) - 垃圾回收</p>
<p>本文参考来源：<a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/memory-management-and-gc" target="_blank" rel="noopener">Memory Management and Garbage Collection in .NET</a> </p>
<p>每次创建新对象时，CLR 都会从托管堆中为该对象分配内存。 </p>
<p>只要托管堆中的地址空间可用，运行时就会继续为新对象分配空间。 </p>
<p>但是，内存不是无限的。最终垃圾收集器必须执行一个回收才能释放一些内存。 </p>
<p>垃圾收集器的优化引擎根据正在进行的内存分配确定执行收集的最佳时间。 </p>
<p>当垃圾收集器执行集合时，它会检查托管堆中不再被应用程序使用的对象，并执行必要的操作来回收其内存。</p>
<h2 id="垃圾回收基础原理"><a href="#垃圾回收基础原理" class="headerlink" title="垃圾回收基础原理"></a>垃圾回收基础原理</h2><h3 id="内存基础原理"><a href="#内存基础原理" class="headerlink" title="内存基础原理"></a>内存基础原理</h3><p>以下列表总结了重要的 CLR 内存概念：</p>
<ol>
<li><p>每个进程都有自己独立的虚拟地址空间。同一台计算机上的所有进程共享相同的物理内存，并共享页面文件（如果有）。</p>
</li>
<li><p>默认情况下，在32位计算机上，每个进程都有一个 2GB 的虚拟地址空间。</p>
</li>
<li><p>作为应用程序开发人员，您只能使用虚拟地址空间，而不能直接操作物理内存。垃圾收集器在托管堆上为您分配和释放虚拟内存。<br>如果您正在编写本机代码，则使用 Win32 函数来处理虚拟地址空间。这些函数在本机堆上为您分配和释放虚拟内存。</p>
</li>
<li><p>虚拟内存可以处于三种状态：</p>
<ul>
<li><p>Free。内存块没有引用它，可用于分配。</p>
</li>
<li><p>Reserved。内存块可供您使用，不能用于任何其他分配请求。但是，在提交之前，您无法将数据存储到此内存块。</p>
</li>
<li><p>Committed。内存块分配给物理存储。</p>
</li>
</ul>
</li>
<li><p>虚拟地址空间可能会碎片化。这意味着地址空间中有空闲块，也称为空洞。当请求分配虚拟内存时，虚拟内存管理器必须找到足够大的单个空闲块以满足该分配请求。即使您有 2GB 的可用空间，除非所有可用空间都在一个地址块中，否则一个 2GB 的分配将不会成功。</p>
</li>
<li><p>如果用尽预留的虚拟地址空间或要提交的物理空间，则可能会耗尽内存。</p>
</li>
</ol>
<p>即使即物理内存需求较低，也会使用您的页面文件。 物理内存需求较高时，操作系统必须在物理内存中腾出空间来存储数据，并将物理内存中的一些数据备份到页面文件中。 </p>
<h3 id="回收时机"><a href="#回收时机" class="headerlink" title="回收时机"></a>回收时机</h3><p>当满足下列条件之一时，进行垃圾回收：</p>
<ol>
<li><p>系统具有较低的物理内存。 这可以通过 OS 的低内存通知或主机指示的低内存来检测。</p>
</li>
<li><p>托管堆上已分配对象使用的内存超过可接受的阈值。 在该过程运行时不断调整该阈值。</p>
</li>
<li><p>调用 GC.Collect 方法。 几乎在所有情况下，您都不必调用此方法，因为垃圾收集器会持续运行。 此方法主要用于特别的情况和测试。</p>
</li>
</ol>
<h3 id="托管堆"><a href="#托管堆" class="headerlink" title="托管堆"></a>托管堆</h3><p>垃圾回收器由 CLR 初始化后，它会分配一段内存来存储和管理对象。 此内存称为托管堆，而不是操作系统中的本机堆。</p>
<p>每个托管进程都有一个托管堆。 进程中的所有线程都为同一堆上的对象分配内存。</p>
<p>为了保留内存，垃圾收集器调用 Win32 VirtualAlloc 函数，并为托管应用程序一次保留一段内存。 垃圾收集器还根据需要保留一片内存片段，并通过调用 Win32 VirtualFree 函数将此内存片段释放回操作系统（在清除对象之后）。</p>
<p>堆上分配的对象越少，垃圾收集器的工作就越少。在分配对象时，请不要使用超出需要的整数值（2的N次方），例如在只需要15个字节时分配32个字节的数组。</p>
<p>当触发垃圾收集时，垃圾收集器将回收垃圾对象占用的内存。回收过程压缩活动对象，使它们移动到一起，并移除垃圾区域，从而使堆更小。这可确保分配在一起的对象在托管堆上保持一致，以保留其位置。</p>
<p>垃圾收集的侵入性（频率和持续时间）由已分配的内存和托管堆上的剩余内存量决定。</p>
<p>堆可以被认为是两个堆的堆积：大对象堆和小对象堆。</p>
<p>大对象堆包含85,000字节或更大的非常大的对象。大对象堆上的对象通常是数组。实例对象很少是非常大的。</p>
<h3 id="垃圾回收的代际"><a href="#垃圾回收的代际" class="headerlink" title="垃圾回收的代际"></a>垃圾回收的代际</h3><p>堆被组织成3代，因此它可以处理长寿命和短寿命的对象。垃圾收集主要发生在回收通常仅占用堆的一小部分的短期对象时。堆上有三代对象：</p>
<ol>
<li><p>第0代。这是最年轻的一代，包含短期对象。典型的短期对象是函数内的临时变量。垃圾收集在这一代中最常发生。<br>新分配的对象一般会形成0代对象，除非它们是大对象，在这种情况下它们在第2代集合中的大对象堆上。<br>大多数对象在第0代被回收用于垃圾收集，并且不能存活到下一代。</p>
</li>
<li><p>第1代。此代包含短期对象，并用作短期对象和长期对象之间的缓冲区。</p>
</li>
<li><p>第2代。这一代包含长寿命对象。长期对象的一个​​典型例子是服务器应用程序中的对象，其中包含在进程持续时间内处于活动状态的静态数据。</p>
</li>
</ol>
<p>在条件允许的情况下，第n代回收，就意味着收集那一代及其所有比它年轻代的对象。所以第2代垃圾回收也称为完全垃圾回收（Full GC），因为它回收了所有代中的所有对象（即托管堆中的所有对象）。</p>
<h4 id="对象存活以及代际提升"><a href="#对象存活以及代际提升" class="headerlink" title="对象存活以及代际提升"></a>对象存活以及代际提升</h4><p>垃圾收集中未回收的对象称为幸存者，并被提升为下一代。 在第0代垃圾收集中存活的对象被提升为第1代; 在第1代垃圾收集中存活的对象被提升为第2代; 在第2代垃圾收集中存活的对象仍然存在。</p>
<p>当垃圾收集器在一代中检测到存活率很高时，它会增加该代的分配阈值，因此下一个集合会获得相当大的回收内存大小。 CLR 会不断平衡：不让应用程序占用内存太大和不让垃圾收集花费太多时间。</p>
<h4 id="Ephemeral-generations-and-segments-短暂代和内存片段"><a href="#Ephemeral-generations-and-segments-短暂代和内存片段" class="headerlink" title="Ephemeral generations and segments 短暂代和内存片段"></a><a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals#ephemeral-generations-and-segments" target="_blank" rel="noopener">Ephemeral generations and segments 短暂代和内存片段</a></h4><h3 id="垃圾回收时发生了什么？"><a href="#垃圾回收时发生了什么？" class="headerlink" title="垃圾回收时发生了什么？"></a>垃圾回收时发生了什么？</h3><p>垃圾收集包含以下阶段：</p>
<ol>
<li><p>标记阶段，用于查找并创建所有活动对象的列表。</p>
</li>
<li><p>重定位阶段，用于更新对将要压缩的对象的引用。</p>
</li>
<li><p>压缩阶段，回收死亡物体占据的空间并压缩幸存的物体。 压缩阶段将在垃圾收集中幸存的对象移动到段的较旧端。<br>由于第2代集合可以占用多个内存段，因此可以将升级为第2代的对象移动到较旧的内存段中。 第1代和第2代幸存者都可以移动到不同的部分，因为它们被提升到第2代。<br>通常，大型对象堆不会被压缩，因为复制大型对象会降低性能。 但是，从.NET Framework 4.5.1开始，您可以使用 GCSettings.LargeObjectHeapCompactionMode 属性按需压缩大对象堆。</p>
</li>
</ol>
<p>垃圾收集器使用以下信息来确定对象是否处于活动状态：</p>
<ol>
<li><p>堆栈根。 堆栈变量由 just-in-time (JIT) compiler 和 stack walker 提供。</p>
</li>
<li><p>垃圾回收句柄。 指向托管对象的句柄，可以由用户代码或公共语言运行库分配。</p>
</li>
<li><p>静态数据。 应用程序域中的静态对象，可以引用其他对象。 每个应用程序域都会跟踪其静态对象。</p>
</li>
</ol>
<p>在垃圾收集开始之前，除了触发垃圾收集的线程之外，所有托管线程都将被挂起。</p>
<p>下图显示了一个触发垃圾收集并导致其他线程被挂起的线程。</p>
<p><img src="/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/gc-triggered.png" alt="执行时序" title="触发垃圾回收"></p>
<h3 id="对象的-Finalize"><a href="#对象的-Finalize" class="headerlink" title="对象的 Finalize"></a>对象的 Finalize</h3><p>当发现可终结对象时，它的 Finalizer 将被放入队列中以便执行其清理操作，但对象本身将被提升为下一代。 因此，您必须等到该生成上发生的下一个垃圾收集（不一定是下一个垃圾收集）来确定该对象是否已被回收。</p>
<h3 id="Workstation-and-server-garbage-collection"><a href="#Workstation-and-server-garbage-collection" class="headerlink" title="Workstation and server garbage collection"></a><a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals#workstation-and-server-garbage-collection" target="_blank" rel="noopener">Workstation and server garbage collection</a></h3><h3 id="Concurrent-garbage-collectio"><a href="#Concurrent-garbage-collectio" class="headerlink" title="Concurrent garbage collectio"></a><a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals#concurrent-garbage-collection" target="_blank" rel="noopener">Concurrent garbage collectio</a></h3><h3 id="Background-workstation-garbage-collection"><a href="#Background-workstation-garbage-collection" class="headerlink" title="Background workstation garbage collection"></a><a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals#background-workstation-garbage-collection" target="_blank" rel="noopener">Background workstation garbage collection</a></h3><h3 id="Background-server-garbage-collection"><a href="#Background-server-garbage-collection" class="headerlink" title="Background server garbage collection"></a><a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals#background-server-garbage-collection" target="_blank" rel="noopener">Background server garbage collection</a></h3><h2 id="大对象堆（large-object-heap）"><a href="#大对象堆（large-object-heap）" class="headerlink" title="大对象堆（large object heap）"></a>大对象堆（large object heap）</h2><p>.NET 垃圾收集器（GC）将对象划分为小型和大型对象。 当一个对象很大时，它的一些属性变得比小对象更重要。 例如，压缩它(将它复制到堆中其他地方的内存中)的代价可能很高。 因此，.NET垃圾收集器将大对象放在大对象堆（LOH）上。 在本主题中，我们将深入研究大对象堆。 我们将讨论什么使对象成为大对象，如何收集这些大对象，以及大对象强加什么样的性能。</p>
<p><strong>注意：本主题讨论仅在 Windows 系统上运行的 .NET Framework 和 .NET Core 中的大对象堆。 它不包括在其他平台上运行 .NET 实现的 LOH 。</strong></p>
<h3 id="对象如何在大对象堆上被回收以及-GC-如何处理它们"><a href="#对象如何在大对象堆上被回收以及-GC-如何处理它们" class="headerlink" title="对象如何在大对象堆上被回收以及 GC 如何处理它们"></a>对象如何在大对象堆上被回收以及 GC 如何处理它们</h3><p>如果一个对象大于或等于85,000字节，则认为它是一个大对象。此数字是根据性能来确定的。当对象分配请求是85,000或更多字节时，运行时将它分配给大对象堆。</p>
<p>要理解这意味着什么，则需要回顾一下关于 .NET GC 的一些基础知识。</p>
<p>.NET垃圾收集器是一代分类收集器。它有三代：第0代，第1代和第2代。拥有3代的原因是，在经过良好调整的应用程序中，大多数对象都在 gen0 中死亡。例如，在服务器应用程序中，与请求关联的分配应在请求完成后消失。正在进行的分配请求将使其成为 gen1 并在那里死亡。从本质上讲，gen1 充当年轻对象区域和长寿命对象区域之间的缓冲区。</p>
<p>小对象总是在第0代中分配，并且根据其生命周期，可以被提升为第1代或第2代。大型对象始终在第2代中分配。</p>
<p>物理上，对象存在于托管堆中。托管堆是 GC 通过托管代码调用 VirtualAlloc 函数从 OS 申请的一块内存。加载 CLR 时，GC会分配两个初始堆段：一个用于小对象（小对象堆，或SOH），另一个用于大对象（大对象堆）。</p>
<p>用户代码只能在第0代（小对象）或LOH（大对象）中分配。只有GC可以在第1代（通过促进第0代幸存者）和第2代（通过提升第1代和第2代的幸存者的代际）“分配”对象。</p>
<p>触发垃圾收集时，GC 会跟踪活动对象并压缩它们。但由于压缩大对象成本很高，所以GC 先扫描 LOH 后，并利用临近 LOH 的垃圾对象，回收他们然他们变成 LOH 空闲空间，来满足大对象分配请求。</p>
<p>.NET Core和.NET Framework（从.NET Framework 4.5.1开始）包括GCSettings.LargeObjectHeapCompactionMode 属性，该属性允许用户指定在下一个完全阻塞 GC 期间应该压缩 LOH。并且在未来，.NET可能会决定自动压缩 LOH。这意味着，如果您分配大型对象并希望确保它们不移动，您仍应将它们固定。</p>
<p>图1说明了 GC 在 Obj1 和 Obj3 已经变为垃圾对象后进行 0代 GC 之后形成第1代的情况。还有 GC 在 Obj2 和 Obj5 已经变为垃圾对象后，执行 1代 GC 之后形成第2代的情况。 请注意，此图和以下图仅用于说明目的; 它们包含的对象非常少，可以更好地显示堆上发生的情况。 实际上，GC中通常涉及更多对象。</p>
<p><img src="/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/loh-figure-1.jpg" alt="图1" title="图1"></p>
<p>图2显示了在 Obj1 和 Obj2 变为垃圾对象，进行 2代 GC。</p>
<p><img src="/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/loh-figure-2.jpg" alt="图2" title="图2"></p>
<p>如果没有足够的可用空间来容纳大对象分配请求，则GC首先尝试从OS获取更多内存段。如果失败，它会触发第2代GC，来释放一些空间。</p>
<h3 id="LOH-性能影响"><a href="#LOH-性能影响" class="headerlink" title="LOH 性能影响"></a>LOH 性能影响</h3><p>大对象堆上的分配以下列方式影响性能。</p>
<ol>
<li><p>分配成本。<br>CLR 确保清除它给出的每个新对象的内存。这意味着大对象的分配成本完全由内存清除控制（除非它触发GC）。如果清除一个字节需要2个周期，则清除最小的大对象需要170,000个周期。在2GHz机器上清除16MB对象的内存大约需要16ms。这是一个相当大的成本。</p>
</li>
<li><p>回收成本。<br>如果由于超过 LOH 大小而触发 2代回收，则在 GC 之后第2代不一定会小得多。如果第2代的数据不多，则影响最小。但是，如果第2代很大，如果触发了许多第2代GC，则可能会导致性能问题。如果许多大型物体是在非常临时的基础上分配的，并且您的SOH很大，那么您可能会花太多时间去做GC。另外，如果你继续分配和释放非常大的对象，分配成本确实会增加。</p>
</li>
<li><p>具有引用类型的数组元素。<br>LOH上的非常大的对象通常是数组（很少有实例对象非常大）。如果数组的元素是引用丰富的，则如果元素不是引用丰富的，则会产生不存在的成本。如果元素不包含任何引用，则垃圾收集器根本不需要遍历该数组。</p>
</li>
</ol>
<p>在这三个因素中，前两个通常比第三个更重要。 因此，我们建议您分配一个可以重用的大型对象池，而不是分配临时对象。</p>
<h2 id="垃圾回收的性能"><a href="#垃圾回收的性能" class="headerlink" title="垃圾回收的性能"></a>垃圾回收的性能</h2><p>本节内容为垃圾回收和内存使用相关的问题。 它解决了与托管堆相关的问题，并解释了如何最小化垃圾回收对应用程序的影响。</p>
<h3 id="性能分析工具"><a href="#性能分析工具" class="headerlink" title="性能分析工具"></a>性能分析工具</h3><h4 id="Memory-Performance-Counters"><a href="#Memory-Performance-Counters" class="headerlink" title="Memory Performance Counters"></a>Memory Performance Counters</h4><p>您可以使用性能计数器来收集性能数据。 有关说明，请参阅 <a href="https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/runtime-profiling" target="_blank" rel="noopener">Runtime Profiling</a>。 .NET CLR 内存类别的性能计数器（<a href="https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/performance-counters" target="_blank" rel="noopener">Performance Counters in the .NET Framework</a>）提供有关垃圾收集器的信息。</p>
<h4 id="Debugging-with-SOS"><a href="#Debugging-with-SOS" class="headerlink" title="Debugging with SOS"></a>Debugging with SOS</h4><p>您可以使用 <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/index" target="_blank" rel="noopener">Windows Debugger (WinDbg)</a> 检查托管堆上的对象。</p>
<p>要安装WinDbg，请从<a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools" target="_blank" rel="noopener">下载Windows调试工具</a>页面安装Windows调试工具。</p>
<h4 id="Garbage-Collection-ETW-Events"><a href="#Garbage-Collection-ETW-Events" class="headerlink" title="Garbage Collection ETW Events"></a>Garbage Collection ETW Events</h4><p>Event tracing for Windows (ETW) 是一个跟踪系统，它补充了 .NET Framework 提供的分析和调试支持。 从.NET Framework 4 开始，垃圾收集 ETW 事件从统计角度捕获用于分析托管堆的有用信息。 例如，垃圾收集即将发生时引发的 GCStart_V1 事件提供以下信息：</p>
<ol>
<li><p>正在收集哪一代对象。</p>
</li>
<li><p>什么引发了垃圾收集。</p>
</li>
<li><p>垃圾收集的类型（并发或非并发）。</p>
</li>
</ol>
<p>ETW 事件日志记录是高效的，不会掩盖与垃圾收集相关的任何性能问题。 进程可以提供自己的事件。 记录时，可以关联应用程序的事件和垃圾回收事件，以确定堆问题的发生方式和时间。 例如，服务器应用程序可以在客户端请求的开始和结束时的事件。</p>
<h4 id="The-Profiling-API"><a href="#The-Profiling-API" class="headerlink" title="The Profiling API"></a>The Profiling API</h4><p>公共语言运行时（CLR）分析界面提供有关在垃圾回收期间受影响的对象的详细信息。 垃圾收集开始和结束时可以通知探查器。 它可以提供有关托管堆上的对象的报告，包括每一代中对象的标识。 有关更多信息，请参阅<a href="https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/profiling-overview" target="_blank" rel="noopener">性能分析概述</a>。</p>
<p>Profilers 可以提供全面的信息。 但是，复杂的分析器可能会修改应用程序的行为。</p>
<h4 id="Application-Domain-Resource-Monitoring"><a href="#Application-Domain-Resource-Monitoring" class="headerlink" title="Application Domain Resource Monitoring"></a>Application Domain Resource Monitoring</h4><p>从.NET Framework 4开始，应用程序域资源监视（ARM）使主机能够按应用程序域监视CPU和内存使用情况。 有关更多信息，请参阅<a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/app-domain-resource-monitoring" target="_blank" rel="noopener">应用程序域资源监视</a>。</p>
<h2 id="执行回收操作的时机"><a href="#执行回收操作的时机" class="headerlink" title="执行回收操作的时机"></a>执行回收操作的时机</h2><p>在大多数情况下，垃圾收集器可以确定执行回收的最佳时间，您应该让它独立运行。强制收集可能会提高应用程序性能的情况很少见。只有在这些情况下，您可以使用 GC.Collect 方法强制垃圾回收来触发垃圾回收。</p>
<p>只有当应用程序代码中的特定点，进行垃圾回收能让使用的内存量显着减少时，才使用 GC.Collect 方法。例如，如果您的应用程序使用具有多个控件的复杂对话框，则在关闭对话框时调用 Collect 可以通过立即回收对话框使用的内存来提高性能。确保您的应用程序不会过于频繁地导致垃圾收集，因为如果垃圾收集器试图在非最佳时间回收对象，则会降低性能。您可以向 Collect 方法提供GCCollectionMode.Optimized 枚举值，以便仅在能回收操作有效时回收。</p>
<h3 id="回收模式"><a href="#回收模式" class="headerlink" title="回收模式"></a>回收模式</h3><p>GCCollectionMode.Default : 采用当前.net版本的默认值。</p>
<p>GCCollectionMode.Force : 强制垃圾回收立即执行。 这相当于调用 GC.Collect。 它导致所有世代的完全阻塞回收。</p>
<p>GCCollectionMode.Optimized : 使垃圾收集器确定当前是否是回收对象的最佳时间。<br>垃圾收集器可以计算当前时间点进行回收的效率，如果效率不够高，它将直接返回而不回收对象。</p>
<h2 id="延迟模式"><a href="#延迟模式" class="headerlink" title="延迟模式"></a>延迟模式</h2><p>要回收对象，垃圾回收器必须停止应用程序中的所有正在执行的线程。 在某些情况下，例如当应用程序检索数据或显示内容时，可能会在关键时刻进行完整的垃圾收集并阻碍性能。 您可以通过将 GCSettings.LatencyMode 属性设置为 System.Runtime.GCLatencyMode 值之一来调整垃圾收集器的侵入性。</p>
<p>延迟是指垃圾收集器侵入您的应用程序的时间。在低延迟期间，垃圾收集器在回收对象时更加保守且侵入性更小。 System.Runtime.GCLatencyMode 枚举提供两个低延迟设置：</p>
<ol>
<li><p>LowLatency禁止 2代回收，仅执行第0代和第1代集合。它只能在短时间内使用。在较长时间内，如果系统处于内存压力下，垃圾收集器将触发收集，这可能会暂停应用程序并中断对时间要求严格的操作。此设置仅适用于 WorkStation 垃圾回收。</p>
</li>
<li><p>SustainedLowLatency 不进行前台的 2代回收，只执行第0代，第1代和第2代后台回收。它可以长时间使用，可用于工作站和服务器垃圾收集。如果禁用并发垃圾回收，则无法使用此设置。</p>
</li>
</ol>
<h2 id="弱引用"><a href="#弱引用" class="headerlink" title="弱引用"></a>弱引用</h2><p>当应用程序的代码可以访问该对象时，垃圾收集器无法收集应用程序正在使用的对象。该应用程序对该对象有强引用。</p>
<p>弱引用允许垃圾收集器收集对象，同时仍允许应用程序访问对象。弱引用仅在不存在强引用时收集对象之前的不确定时间内有效。当您使用弱引用时，应用程序仍然可以获得对该对象的强引用，从而阻止其被收集。但是，在重新建立强引用之前，始终存在垃圾收集器首先到达对象的风险。</p>
<p>弱引用对于被垃圾回收回收后可以轻松地重新创建的，而且使用大量内存的对象很有用。</p>
<p>假设Windows窗体应用程序中的树视图向用户显示复杂的分层选项选项。如果底层数据很大，当用户涉及应用程序中的其他内容时，将树保留在内存中效率很低。</p>
<p>当用户切换到应用程序的另一部分时，您可以使用WeakReference类创建对树的弱引用并销毁所有强引用。当用户切换回树时，应用程序尝试获取对树的强引用，如果成功，则避免重构树。</p>
<p>要使用对象建立弱引用，可以使用要跟踪的对象的实例创建WeakReference。然后，将Target属性设置为该对象，并将对象的原始引用设置为null。有关代码示例，请参阅类库中的<a href="https://docs.microsoft.com/en-us/dotnet/api/system.weakreference" target="_blank" rel="noopener">WeakReference</a>。</p>
<h3 id="短期和长期弱引用"><a href="#短期和长期弱引用" class="headerlink" title="短期和长期弱引用"></a>短期和长期弱引用</h3><p>您可以创建一个简短的弱引用或一个长弱引用：</p>
<ol>
<li><p>短期：<br>当垃圾收集回收对象时，短弱引用的目标变为 null。弱引用本身就是一个托管对象，并且像任何其他托管对象一样受垃圾收集的影响。一个简短的弱引用是 WeakReference 的默认构造函数。</p>
</li>
<li><p>长期：<br>调用对象的 Finalize 方法后，将保留长期的弱引用。这允许重新创建对象，但对象的状态仍然是不可预测的。要使用长引用，请在 WeakReference 构造函数中指定 true。</p>
</li>
</ol>
<p>如果对象的类型没有Finalize方法，则应用短弱引用功能，并且弱引用仅在收集目标之前有效，这可以在终结器运行后的任何时间发生。</p>
<p>要建立强引用并再次使用该对象，请将 WeakReference 的 Target 属性强制转换为对象的类型。如果 Target 属性返回 null ，则该对象已被回收;否则，您可以继续使用该对象，因为该应用程序已重新获得对它的强引用。</p>
<h3 id="使用弱引用的指南"><a href="#使用弱引用的指南" class="headerlink" title="使用弱引用的指南"></a>使用弱引用的指南</h3><p>仅在必要时使用长期弱引用，因为在完成后对象的状态是不可预测的。</p>
<p>避免对小对象使用弱引用，因为指针本身可能和对象一样大或更大。</p>
<p>避免使用弱引用作为内存管理问题的自动解决方案。 相反，开发一个有效的缓存策略来处理应用程序的对象。</p>
<h2 id="Optimization-for-Shared-Web-Hosting"><a href="#Optimization-for-Shared-Web-Hosting" class="headerlink" title="Optimization for Shared Web Hosting"></a><a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/optimization-for-shared-web-hosting" target="_blank" rel="noopener">Optimization for Shared Web Hosting</a></h2>
      
    </div>

    

    
    
    

    

    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/my_blog/tags/CSharp/" rel="tag"># CSharp</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/my_blog/2018/08/10/DotNet-Guide-Memory-Management/" rel="next" title="Memory Management and Garbage Collection in .NET - 1">
                <i class="fa fa-chevron-left"></i> Memory Management and Garbage Collection in .NET - 1
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            Inhaltsverzeichnis
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            Übersicht
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name">Raymond</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          
            <nav class="site-state motion-element">
              
                <div class="site-state-item site-state-posts">
                
                  <a href="/my_blog/archives/">
                
                    <span class="site-state-item-count">12</span>
                    <span class="site-state-item-name">Artikel</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-categories">
                  <a href="/my_blog/categories/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">5</span>
                    <span class="site-state-item-name">Kategorien</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-tags">
                  <a href="/my_blog/Tags/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">11</span>
                    <span class="site-state-item-name">Tags</span>
                  </a>
                </div>
              
            </nav>
          

          

          

          
          

          
          

          
            
          
          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#垃圾回收基础原理"><span class="nav-number">1.</span> <span class="nav-text">垃圾回收基础原理</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#内存基础原理"><span class="nav-number">1.1.</span> <span class="nav-text">内存基础原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#回收时机"><span class="nav-number">1.2.</span> <span class="nav-text">回收时机</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#托管堆"><span class="nav-number">1.3.</span> <span class="nav-text">托管堆</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#垃圾回收的代际"><span class="nav-number">1.4.</span> <span class="nav-text">垃圾回收的代际</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#对象存活以及代际提升"><span class="nav-number">1.4.1.</span> <span class="nav-text">对象存活以及代际提升</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Ephemeral-generations-and-segments-短暂代和内存片段"><span class="nav-number">1.4.2.</span> <span class="nav-text">Ephemeral generations and segments 短暂代和内存片段</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#垃圾回收时发生了什么？"><span class="nav-number">1.5.</span> <span class="nav-text">垃圾回收时发生了什么？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#对象的-Finalize"><span class="nav-number">1.6.</span> <span class="nav-text">对象的 Finalize</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Workstation-and-server-garbage-collection"><span class="nav-number">1.7.</span> <span class="nav-text">Workstation and server garbage collection</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Concurrent-garbage-collectio"><span class="nav-number">1.8.</span> <span class="nav-text">Concurrent garbage collectio</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Background-workstation-garbage-collection"><span class="nav-number">1.9.</span> <span class="nav-text">Background workstation garbage collection</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Background-server-garbage-collection"><span class="nav-number">1.10.</span> <span class="nav-text">Background server garbage collection</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#大对象堆（large-object-heap）"><span class="nav-number">2.</span> <span class="nav-text">大对象堆（large object heap）</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#对象如何在大对象堆上被回收以及-GC-如何处理它们"><span class="nav-number">2.1.</span> <span class="nav-text">对象如何在大对象堆上被回收以及 GC 如何处理它们</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#LOH-性能影响"><span class="nav-number">2.2.</span> <span class="nav-text">LOH 性能影响</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#垃圾回收的性能"><span class="nav-number">3.</span> <span class="nav-text">垃圾回收的性能</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#性能分析工具"><span class="nav-number">3.1.</span> <span class="nav-text">性能分析工具</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#Memory-Performance-Counters"><span class="nav-number">3.1.1.</span> <span class="nav-text">Memory Performance Counters</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Debugging-with-SOS"><span class="nav-number">3.1.2.</span> <span class="nav-text">Debugging with SOS</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Garbage-Collection-ETW-Events"><span class="nav-number">3.1.3.</span> <span class="nav-text">Garbage Collection ETW Events</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#The-Profiling-API"><span class="nav-number">3.1.4.</span> <span class="nav-text">The Profiling API</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Application-Domain-Resource-Monitoring"><span class="nav-number">3.1.5.</span> <span class="nav-text">Application Domain Resource Monitoring</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#执行回收操作的时机"><span class="nav-number">4.</span> <span class="nav-text">执行回收操作的时机</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#回收模式"><span class="nav-number">4.1.</span> <span class="nav-text">回收模式</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#延迟模式"><span class="nav-number">5.</span> <span class="nav-text">延迟模式</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#弱引用"><span class="nav-number">6.</span> <span class="nav-text">弱引用</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#短期和长期弱引用"><span class="nav-number">6.1.</span> <span class="nav-text">短期和长期弱引用</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#使用弱引用的指南"><span class="nav-number">6.2.</span> <span class="nav-text">使用弱引用的指南</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Optimization-for-Shared-Web-Hosting"><span class="nav-number">7.</span> <span class="nav-text">Optimization for Shared Web Hosting</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2018</span>
  <span class="with-love" id="animate">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Raymond</span>

  

  
</div>




  <div class="powered-by">Erstellt mit  <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> v3.7.1</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">Theme &mdash; <a class="theme-link" target="_blank" href="https://theme-next.org">NexT.Pisces</a> v6.3.0</div>




        








        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>


























  
  
    <script type="text/javascript" src="/my_blog/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/my_blog/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/my_blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  


  


  <script type="text/javascript" src="/my_blog/js/src/utils.js?v=6.3.0"></script>

  <script type="text/javascript" src="/my_blog/js/src/motion.js?v=6.3.0"></script>



  
  


  <script type="text/javascript" src="/my_blog/js/src/affix.js?v=6.3.0"></script>

  <script type="text/javascript" src="/my_blog/js/src/schemes/pisces.js?v=6.3.0"></script>



  
  <script type="text/javascript" src="/my_blog/js/src/scrollspy.js?v=6.3.0"></script>
<script type="text/javascript" src="/my_blog/js/src/post-details.js?v=6.3.0"></script>



  


  <script type="text/javascript" src="/my_blog/js/src/bootstrap.js?v=6.3.0"></script>



  



	





  





  










  





  

  

  

  

  
  

  

  

  

  

  

</body>
</html>
